﻿using gt.IdentityServer.Models;
using gt.IdentityServer.Services;
using IdentityModel;
using IdentityServer4;
using IdentityServer4.Extensions;
using IdentityServer4.Services;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace gt.IdentityServer.Controllers
{
    [AllowAnonymous]
    public class AccountController : Controller
    {
        private readonly IIdentityServerInteractionService _interaction;
        private readonly ILoginService _loginService;

        public AccountController(IIdentityServerInteractionService interaction,
            ILoginService loginService)
        {
            _interaction = interaction;
            _loginService = loginService;
        }

        [HttpGet]
        public async Task<IActionResult> Login(string returnUrl)
        {
            var flag = _interaction.IsValidReturnUrl(returnUrl);
            if (!flag)
            {
                //error
            }
            var vm = new LoginViewModel
            {
                AllowRememberLogin = true,
                ReturnUrl = returnUrl,
            };

            await Task.CompletedTask;

            return View(vm);
        }

        [HttpPost]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> Login(LoginInputModel mode, string button)
        {
            var context = await _interaction.GetAuthorizationContextAsync(mode.ReturnUrl);
            //if (button != "login")
            //{ }
            if (ModelState.IsValid)
            {
                var user = await _loginService.FindLoginUserAsync(mode.Username, mode.Password);
                if (user == null)
                {
                    ModelState.AddModelError(string.Empty, "error username or password");
                }
                AuthenticationProperties props = null;
                if (mode.RememberLogin)
                {
                    props = new AuthenticationProperties
                    {
                        IsPersistent = true,
                        ExpiresUtc = DateTimeOffset.UtcNow.Add(TimeSpan.FromHours(1))
                    };
                }

                var isuser = new IdentityServerUser(user.Id.ToString()) { DisplayName = user.UserName };
                await HttpContext.SignInAsync(isuser, props);
                if (context != null)
                {
                    return Redirect(mode.ReturnUrl);
                }
                if (Url.IsLocalUrl(mode.ReturnUrl))
                    return Redirect(mode.ReturnUrl);
                else if (string.IsNullOrEmpty(mode.ReturnUrl))
                    return Redirect("~/");
                else
                    throw new Exception("invalid return url");
            }

            var vm = new LoginViewModel
            {
                ReturnUrl = mode.ReturnUrl,
                Username = mode.Username,
                AllowRememberLogin = true,
                RememberLogin = mode.RememberLogin
            };
            return View(vm);
        }

        /// <summary>
        /// Show logout page
        /// </summary>
        [HttpGet]
        public async Task<IActionResult> Logout(string logoutId)
        {
            // build a model so the logout page knows what to display
            var vm = new LogoutViewModel { LogoutId = logoutId };

            if (vm.ShowLogoutPrompt == false)
            {
                // if the request for logout was properly authenticated from IdentityServer, then
                // we don't need to show the prompt and can just log the user out directly.
                return await Logout(vm);
            }

            return View(vm);
        }

        /// <summary>
        /// Handle logout page postback
        /// </summary>
        [HttpPost]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> Logout(LogoutInputModel model)
        {
            // build a model so the logged out page knows what to display
            var vm = await BuildLoggedOutViewModelAsync(model.LogoutId);

            if (User?.Identity.IsAuthenticated == true)
            {
                // delete local authentication cookie
                await HttpContext.SignOutAsync();

                // raise the logout event
                //await _events.RaiseAsync(new UserLogoutSuccessEvent(User.GetSubjectId(), User.GetDisplayName()));
            }

            // check if we need to trigger sign-out at an upstream identity provider
            if (vm.TriggerExternalSignout)
            {
                // build a return URL so the upstream provider will redirect back
                // to us after the user has logged out. this allows us to then
                // complete our single sign-out processing.
                string url = Url.Action("Logout", new { logoutId = vm.LogoutId });

                // this triggers a redirect to the external provider for sign-out
                return SignOut(new AuthenticationProperties { RedirectUri = url }, vm.ExternalAuthenticationScheme);
            }

            return View("LoggedOut", vm);
        }

        [HttpGet]
        public IActionResult AccessDenied()
        {
            return View();
        }

        private async Task<LoggedOutViewModel> BuildLoggedOutViewModelAsync(string logoutId)
        {
            // get context information (client name, post logout redirect URI and iframe for federated signout)
            var logout = await _interaction.GetLogoutContextAsync(logoutId);

            var vm = new LoggedOutViewModel
            {
                AutomaticRedirectAfterSignOut = true,
                PostLogoutRedirectUri = logout?.PostLogoutRedirectUri,
                ClientName = string.IsNullOrEmpty(logout?.ClientName) ? logout?.ClientId : logout?.ClientName,
                SignOutIframeUrl = logout?.SignOutIFrameUrl,
                LogoutId = logoutId
            };

            if (User?.Identity.IsAuthenticated == true)
            {
                var idp = User.FindFirst(JwtClaimTypes.IdentityProvider)?.Value;
                if (idp != null && idp != IdentityServer4.IdentityServerConstants.LocalIdentityProvider)
                {
                    var providerSupportsSignout = await HttpContext.GetSchemeSupportsSignOutAsync(idp);
                    if (providerSupportsSignout)
                    {
                        if (vm.LogoutId == null)
                        {
                            // if there's no current logout context, we need to create one
                            // this captures necessary info from the current logged in user
                            // before we signout and redirect away to the external IdP for signout
                            vm.LogoutId = await _interaction.CreateLogoutContextAsync();
                        }

                        vm.ExternalAuthenticationScheme = idp;
                    }
                }
            }

            return vm;
        }

    }
}
